#include <atomic>
#include <chrono>
#include <condition_variable>
#include <dirent.h>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <memory>
#include <mutex>
#include <ostream>
#include <queue>
#include <regex>
#include <sstream>
#include <unordered_map>
#include <utility>
#include <vector>
#include <thread>

#include "config.h"
#include "datacollector.hh"
#include "diagconfig.h"
#include "diagosapi.h"
#include "jkutil/iniparser.h"
#include "logqueue.hpp"
#include "zuclog.h"

class DiagConfig;
class DataCollectorContext;
class DataCollector;

using DataCollectorConfigPtr = std::shared_ptr<DiagConfig>;
using TimePoint = std::chrono::time_point<std::chrono::steady_clock>;
using TimedCANMsgType = std::pair<TimePoint, zucDiagBusMsg>;
using TimedCANMsgQueueType = SimpleQueue<TimedCANMsgType, true>;
using CANMsgType = zucDiagBusMsg;
using CANMsgQueueType = SimpleQueue<CANMsgType, true>;

#define DIAG_KEY_STR(KEY) "DIAGNOSIS." #KEY

const std::string DiagConfig::DIAG_BASE_DUR_KEY = DIAG_KEY_STR(DIAG_BASE_DUR_KEY);
const std::string DiagConfig::DIAG_MAX_DUR_KEY = DIAG_KEY_STR(DIAG_MAX_DUR_KEY);
const std::string DiagConfig::DIAG_STEP_KEY = DIAG_KEY_STR(DIAG_STEP_KEY);
const std::string DiagConfig::DIAG_MAX_FILE_BYTES_KEY = DIAG_KEY_STR(DIAG_MAX_FILE_BYTES_KEY);

/* 基础日志记录时长: 默认60s */
constexpr uint32_t DiagConfig::DEF_DIAG_BASE_DUR;
/* 最大诊断记录时长: 默认180s */
constexpr uint32_t DiagConfig::DEF_DIAG_MAX_DUR;
/* 诊断记录时间步长: 默认30s */
constexpr uint32_t DiagConfig::DEF_DIAG_STEP_DUR;
/* 最大诊断磁盘占用限制: 1024M */
constexpr uint32_t DiagConfig::DEF_DIAG_MAX_FILE_BYTES;

const DiagOSAPI::ListPath<DiagOSAPI::ListFileCBFunc> DiagOSAPI::ListFile = ListPath<ListFileCBFunc>();
const DiagOSAPI::ListPath<DiagOSAPI::ListDirCBFunc> DiagOSAPI::ListDir = ListPath<ListDirCBFunc>();

class DataCollectorContext
{
public:
    enum class TriggerType
    {
        MANUAL = 0,
        AUTO
    };

    enum class DiagStopReason
    {
        // 超时停止
        TIMEOUT_STOP,
        // 手动触发停止
        MANUAL_STOP
    };

    DataCollectorContext(uint32_t ecode);
    ~DataCollectorContext();

    DataCollectorContext(const DataCollectorContext&) = delete;
    DataCollectorContext operator=(DataCollectorContext&) = delete;

    // 是否应该停止当前诊断
    bool ShouldStopDiag() const { return should_stop_diag; }
    void StopDiag(DiagStopReason reason)
    {
        if (should_stop_diag)
        {
            return;
        }

        if (reason == DiagStopReason::MANUAL_STOP)
        {
            this->end_time = std::chrono::steady_clock::now();
        }

        should_stop_diag = true;
    }

    // 对当前诊断延时
    void Delay();

    // 判断当前诊断是否已经到停止时间
    bool CheckTime(const TimePoint timePoint) { return timePoint >= start_time.load() && timePoint <= end_time.load(); }
    bool CheckTimeExceedUpperLimit(const TimePoint timePoint) { return timePoint > end_time.load(); }
    bool CheckTimeExceedLowerLimit(const TimePoint timePoint) { return timePoint < start_time.load(); }

    // 触发本次诊断的错误码, 手动触发则为0
    uint32_t Ecode() const { return ecode; }

    // 手动触发和自动触发
    TriggerType GetTriggerType() const { return type; }

    TimePoint GetTriggerTime() const { return trigger_time; }

private:
    constexpr static int64_t MAX_CANMSG_NUM = 60000;

    /* 诊断触发时刻 */
    const TimePoint trigger_time;
    /* 报文开始记录时间 */
    std::atomic<TimePoint> start_time;
    /* 报文记录时间 */
    std::atomic<TimePoint> end_time;

    std::atomic_bool should_stop_diag;
    TriggerType type;

    /* 触发诊断时的错误码 */
    uint32_t ecode;
};

class DataCollectorDumpWorker
{
private:
    struct DiagFileInfo
    {
        std::string id;
        std::string date;
        std::string errHex;
    };

    using FileInfoPair = std::pair<DiagFileInfo, std::string>;

public:
    DataCollectorDumpWorker()
    {
        loopStop.store(false);
        pthread_create(
            &innerThread,
            NULL,
            [](void* p) -> void* {
                auto self = reinterpret_cast<DataCollectorDumpWorker*>(p);
                if (self)
                {
                    pthread_setname_np(pthread_self(), "zucdiagdump");
                    self->LoadMaxDiagFileId();
                    self->Loop();
                }
                return NULL;
            },
            this);
    }

    ~DataCollectorDumpWorker()
    {
        loopStop.store(true);
        cv_mtx.lock();
        cv.notify_one();
        cv_mtx.unlock();
        pthread_join(innerThread, NULL);
    }

    void SetContext(std::shared_ptr<DataCollectorContext> p_context_)
    {
        cv_mtx.lock();
        p_ctx = p_context_;
        cv_mtx.unlock();
        cv.notify_one();
    }

private:
    void Loop();

    void DumpSysStatus(const std::string& filePath, const std::shared_ptr<DataCollector>& pCollector)
    {
        std::ofstream sysInfoFs(filePath, std::ios::trunc);
        if (sysInfoFs)
        {
            // 系统信息
            sysInfoFs << "======================= OS Info ======================= \n";
            DiagOSAPI::OSInfo(sysInfoFs);
            sysInfoFs << "\n\n======================= TopCpuInfo "
                         "======================= \n";
            DiagOSAPI::TopCpuLog(sysInfoFs);
            sysInfoFs << "\n\n======================= TopMemInfo "
                         "======================= \n";
            DiagOSAPI::TopMemLog(sysInfoFs);
            sysInfoFs << "\n\n======================= TopDiskLog "
                         "======================= \n";
            DiagOSAPI::IOTopDiskLog(sysInfoFs);
            sysInfoFs << "\n\n";
            sysInfoFs << "\n\n======================= Hardware ======================= \n";
            DumpHardwareStatus(sysInfoFs, pCollector);
        }
        sysInfoFs.close();
    }

    void DumpHardwareStatus(std::ostream& os, const std::shared_ptr<DataCollector>& pCollector);

    void CollectSysInfo(const std::shared_ptr<DataCollector>& pCollector, std::shared_ptr<DataCollectorContext>& ctx, const std::string& dir)
    {
        zucInnerDbg(">> Collect Sys Info ...\n");
        std::string sysInfoFile = dir + "/sysinfo.txt";
        DumpSysStatus(sysInfoFile, pCollector);
        DiagOSAPI::CopyFile("/JKOS.ini", dir);

        // 机器信息
        DiagOSAPI::CopyFile(ZUC_USER_CONFIG_FILE(controller_box_FBT.b32), dir);
        DiagOSAPI::CopyFile(ZUC_USER_CONFIG_FILE(controller_box_FBT.md5), dir);
        DiagOSAPI::CopyFile(ZUC_USER_CONFIG_FILE(robot_serial_num.ini), dir);
    }

    // 收集can报文
    void CollectCanData(std::shared_ptr<DataCollectorContext>& ctx, const std::string& dir);

    void CollectLog(std::shared_ptr<DataCollectorContext>& ctx, const std::string& dir)
    {
        zucInnerDbg(">> Collect log ...\n");
        // 日志
        DiagOSAPI::CopyFile(ZUC_SCRIPT_DIR "/servoUpgrade/appRun.txt", dir);
        DiagOSAPI::CopyFile(ZUC_DEFAULT_ROOT_DIR "/JKZUC_INFO.ini", dir);
        DiagOSAPI::CopyFile(ZUC_LOG_DIR "/errorlog*", dir);
        DiagOSAPI::CopyFile(ZUC_LOG_DIR "/zucdbg*.log", dir);

        DiagOSAPI::CopyFile(ZUC_LOG_DIR "/sdk.log", dir);
        DiagOSAPI::CopyFile(ZUC_LOG_DIR "/sdk.log.*.gz", dir);

        DiagOSAPI::CopyFile(ZUC_LOG_DIR "/grpc.log", dir);
        DiagOSAPI::CopyFile(ZUC_LOG_DIR "/grpc.log.*.gz", dir);

        DiagOSAPI::CopyFile(ZUC_LOG_DIR "/addon.log", dir);
        DiagOSAPI::CopyFile(ZUC_LOG_DIR "/addon.log.*.gz", dir);

        DiagOSAPI::CopyFile(ZUC_LOG_DIR "/jaka_upgrade.log", dir);

        std::ofstream syslogfs(dir + "/syslog", std::ios::trunc);
        if (syslogfs)
        {
            DiagOSAPI::DumpSysLog(syslogfs, 4, 8000);
        }
        syslogfs.close();
    }

    void CollectUerConfig(std::shared_ptr<DataCollectorContext>& ctx, const std::string& dir)
    {
        zucInnerDbg(">> Collect user config ...\n");
        // 用户数据
        DiagOSAPI::CopyFile(ZUC_USER_CONFIG_FILE(usersettings.ini), dir);
        DiagOSAPI::CopyFile(ZUC_USER_CONFIG_FILE(aliasname.ini), dir);
        DiagOSAPI::CopyFile(ZUC_USER_CONFIG_FILE(calibrationsettings.ini), dir);
        DiagOSAPI::CopyFile(ZUC_USER_CONFIG_FILE(safezone.xml), dir);
        DiagOSAPI::CopyFile(ZUC_USER_CONFIG_FILE(checksum.ini), dir);
        DiagOSAPI::CopyFile(ZUC_USER_CONFIG_FILE(extiosetup.xml), dir);
        DiagOSAPI::CopyFile(ZUC_USER_CONFIG_FILE(optionalInfoConfig.ini), dir);
        DiagOSAPI::CopyFile(ZUC_USER_CONFIG_FILE(rs274ngc.var), dir);
        DiagOSAPI::CopyFile(ZUC_USER_CONFIG_FILE(rs274ngc.var.bak), dir);
        DiagOSAPI::CopyFile(ZUC_USER_CONFIG_FILE(tiosignals.xml), dir);
        DiagOSAPI::CopyFile(ZUC_USER_CONFIG_FILE(trajconfig.ini), dir);
    }

    void CollectProgram(std::shared_ptr<DataCollectorContext>& ctx, const std::string& dir);

    void CompressDir(const std::string& cwd, const std::string& diagDirName) { DiagOSAPI::PackTargz(cwd, diagDirName, diagDirName); }

    static std::string Hex(uint32_t ecode)
    {
        std::stringstream stream;
        stream << std::uppercase << std::setfill('0') << std::setw(6) << std::hex << ecode;
        return stream.str();
    }

    static std::string FormatDiagFileName(uint64_t id, uint32_t ecode)
    {
        char buffer[32];
        time_t rawtime;
        struct tm* timeinfo;
        time(&rawtime);
        timeinfo = localtime(&rawtime);
        strftime(buffer, sizeof(buffer), "%Y%m%d%H%M%S", timeinfo);
        std::string dirname = "ID" + std::to_string(id) + '_' + std::string(buffer);
        if (ecode != 0)
        {
            dirname = dirname + "_ERR" + Hex(ecode);
        }
        return dirname;
    }

    struct PQTypeGreater
    {
    private:
        uint64_t toU64Id(const std::string& str) const
        {
            uint64_t ret;
            try
            {
                ret = std::stoull(str);
            }
            catch (const std::logic_error& e)
            {
                ret = 0;
            }
            return ret;
        }

    public:
        bool operator()(const FileInfoPair& pairX, const FileInfoPair& pairY) const
        {
            // 日期长度不为14, 按date字符串排序
            // 一个为14， 另一个不为14，优先删除非14
            // 均为14, 无id时, 根据date排序删除
            // 均为14, 优先删除无id， 后根据id序删除
            const DiagFileInfo& x = pairX.first;
            const DiagFileInfo& y = pairY.first;

            if (x.date.size() != y.date.size())
            {
                // 检查日期长度是否为14
                if (x.date.size() == 14)
                {
                    return true;
                }

                if (y.date.size() == 14)
                {
                    return false;
                }

                return x.date > y.date;
            }

            if (x.date.size() != 14)
            {
                return x.date > y.date;
            }

            uint64_t idX = toU64Id(x.id);
            uint64_t idY = toU64Id(y.id);

            // id为空时, 按时间先后删除
            if (x.id.size() != y.id.size())
            {
                if (x.id.size() == 0)
                {
                    return false;
                }

                if (y.id.size() == 0)
                {
                    return true;
                }

                return idX > idY;
            }

            if (x.id.size() == 0)
            {
                return x.date > y.date;
            }

            return idX > idY;
        }
    };

    static int32_t ParseDiagFileInfo(const std::string& fileName, struct DiagFileInfo& fileInfo)
    {
        int32_t ret;
        // 正则表达式匹配日期、ID和16进制数部分
        std::regex re("^(?:ID([0-9]+)_)?(\\d{14})(?:_ERR([0-9a-fA-F]{6}))?\\.tar\\.gz$");

        std::smatch match;
        if (std::regex_match(fileName, match, re))
        {
            fileInfo.id = match.str(1);      // 提取ID部分
            fileInfo.date = match.str(2);    // 提取日期部分
            fileInfo.errHex = match.str(3);  // 提取ERR后的16进制数部分
            ret = 0;
        }
        else
        {
            // 解析失败，设置为空字符串
            fileInfo.date = "";
            fileInfo.id = "";
            fileInfo.errHex = "";
            ret = -1;
        }
        return ret;
    }

    void LoadMaxDiagFileId();

    int32_t RotateDiagnosisPack(const std::string& curWorkDir, uint64_t diagFileUpperLimit);

    void WriteCANMsg(std::ostream& os, const zucDiagBusMsg& canMsg)
    {
        if (!os)
            os.clear();

        os << "CAN1_" << std::dec << canMsg.mtnNo << ",";
        if (canMsg.is_tx == false)
        {
            os << "RX,";
        }
        else
        {
            os << "TX,";
        }
        if (canMsg.is_ext)
        {
            os << "ExtID,";
        }
        else
        {
            os << "StdID,";
        }
        os << "0x" << std::hex << canMsg.id << ",";
        os << std::dec << canMsg.timestamp_ << ",";
        os << "x| ";
        for (size_t i = 0; i < canMsg.size; i++) { os << std::setfill('0') << std::setw(2) << std::hex << int(canMsg.data[i]) << " "; }
        os << std::endl;
    }

private:
    pthread_t innerThread;
    std::atomic_bool loopStop;
    std::mutex cv_mtx;
    std::condition_variable cv;
    std::shared_ptr<DataCollectorContext> p_ctx;
    uint64_t maxDiagFileId;
};

class DataCollectorErrcodeWorker
{
    using QueueType = SimpleQueue<uint32_t, false>;

public:
    DataCollectorErrcodeWorker() : queue(QUEUE_SIZE)
    {
        loopStop = false;
        pthread_create(
            &innerThread,
            NULL,
            [](void* p) -> void* {
                auto self = reinterpret_cast<DataCollectorErrcodeWorker*>(p);
                if (self)
                {
                    pthread_setname_np(pthread_self(), "zucdiagecode");
                    self->Loop();
                }
                return NULL;
            },
            this);
    }

    ~DataCollectorErrcodeWorker()
    {
        loopStop = true;
        cv_mtx.lock();
        cv.notify_one();
        cv_mtx.unlock();
        pthread_join(innerThread, NULL);
    }

    int32_t Push(uint32_t errcode)
    {
        cv_mtx.lock();
        if (queue.Push(errcode) != 0)
        {
            queue.Pop(errcode);
            queue.Push(errcode);
        }
        cv_mtx.unlock();
        cv.notify_one();
        return 0;
    }

private:
    void Loop();

private:
    static constexpr int32_t QUEUE_SIZE = 64;

    std::atomic_bool loopStop;
    pthread_t innerThread;
    std::mutex cv_mtx;
    std::condition_variable cv;

    QueueType queue;
};

// 处理所有诊断相关请求
class DataCollector
{
public:
    const static std::string BASE_DIAGNOSIS_DIR;
    zucDiagSCBVersion scb_ver_;
    zucDiagServoVersion servo_ver_;

    ~DataCollector() { zucInnerDbg("DataCollector Des !\n"); }
    DataCollector(const DataCollector&) = delete;
    DataCollector& operator=(const DataCollector&) = delete;

public:
    // 初始化诊断(包括各个子线程)
    static int32_t Init()
    {
        if (!s_instance_)  // 防止锁的性能消耗
        {
            s_instance_mtx_.lock();
            if (!s_instance_)  // 确保仅在锁内初始化
            {
                s_instance_ = std::shared_ptr<DataCollector>(new DataCollector);
                s_instance_->diag_config_.Init(ZUC_USER_CONFIG_FILE(usersettings.ini));
            }
            s_instance_mtx_.unlock();
        }
        return 0;
    }

    // 诊断退出(包括各个子线程)
    static int32_t Reset()
    {
        if (s_instance_)
        {
            s_instance_mtx_.lock();
            if (s_instance_)
            {
                s_instance_.reset();
            }
            s_instance_mtx_.unlock();
        }
        return 0;
    }

    // 暴露给模块内部调用, 全局仅仅一个实例
    static std::weak_ptr<DataCollector> Ref() { return s_instance_; }

    // 线程安全
    int32_t Start(uint32_t ecode)
    {
        // ctx表示一次诊断的上下文数据

        // 诊断开始时, 判断是不存在ctx对象, 则没有诊断在执行
        auto p_ctx = p_context_.lock();
        if (!p_ctx)
        {
            std::lock_guard<std::mutex> lk(ctx_gen_mtx_);
            p_ctx = p_context_.lock();
            if (!p_ctx)
            {
                zucInnerDbg("Start New ...\n");
                p_ctx = std::make_shared<DataCollectorContext>(ecode);
                p_context_ = p_ctx;
                dump_worker_.SetContext(p_ctx);
                return 0;
            }
        }

        // 当前已经触发停止诊断，但诊断未结束时
        if (p_ctx->ShouldStopDiag())
        {
            zucInnerDbg("Failed: Already stop diaging ...\n");
            return -1;
        }

        // 当前处于诊断中，尝试延时
        zucInnerDbg("Start Delay...\n");
        p_ctx->Delay();
        return 0;
    }

    // 线程安全
    int32_t Stop()
    {
        auto p_ctx = p_context_.lock();

        // 诊断开始时, 判断是不存在ctx对象, 则没有诊断在执行
        if (!p_ctx)
        {
            zucInnerDbg("Already stop diag ...\n");
            return -1;
        }

        // 当前处于诊断中，请求停止诊断
        zucInnerDbg("Stop diag ...\n");
        p_ctx->StopDiag(DataCollectorContext::DiagStopReason::MANUAL_STOP);
        return 0;
    }

    bool IsCollecting()
    {
        // tx指向的对象已被释放, 来表示未在诊断中
        return !p_context_.expired();
    }

    int32_t Push(const zucDiagBusMsg& canMsg)
    {
        auto time_point = std::chrono::steady_clock::now();
        auto timedCANMsg = std::make_pair(time_point, canMsg);
        if (can_msg_queue.Push(timedCANMsg) != 0)
        {
            TimedCANMsgType canMsg;
            can_msg_queue.Pop(canMsg);
            can_msg_queue.Push(timedCANMsg);
        }
        return 0;
    }

    TimedCANMsgQueueType* CANMsgQueue() { return &can_msg_queue; }

    int32_t Push(uint32_t errcode)
    {
        // 收到错误码触发诊断时，先根据EcodeDiagMap中状态位判断,
        // 满足条件后再推入错误码队列
        auto iter = EcodeDiagFlagMap.find(errcode);

        if (iter == EcodeDiagFlagMap.end())
        {
            return -1;
        }

        if (iter->second == DiagnosisEnum::NO_DIAGNOSIS)
        {
            return -1;
        }

        return errcode_worker_.Push(errcode);
    }

    int32_t SetLoadedProgName(const std::string& progName)
    {
        // 设置当前已经加载的程序
        std::unique_lock<std::mutex> lk(loaded_program_mtx_);
        loaded_prog_name = progName;
        return 0;
    }

    const std::string& GetLoadedProgName()
    {
        std::unique_lock<std::mutex> lk(loaded_program_mtx_);
        return loaded_prog_name;
    }

    const DiagConfig& GetConfig() const { return diag_config_; }
    DiagConfig& GetConfig() { return diag_config_; }

private:
    DataCollector() : can_msg_queue(MAX_CANMSG_NUM), loaded_prog_name("") { zucInnerDbg("DataCollector Run !\n"); }
    enum class DiagnosisEnum
    {
        NO_DIAGNOSIS = 0,
        DIAGNOSIS = 1
    };

    using FlagMapType = const std::unordered_map<uint32_t, DiagnosisEnum>;
    const FlagMapType EcodeDiagFlagMap = {
#include "diagecode.h"
    };
    // CAN报文队列上限
    constexpr static int64_t MAX_CANMSG_NUM = 60000;
    static std::shared_ptr<DataCollector> s_instance_;
    // DataCollector单例锁
    static std::mutex s_instance_mtx_;
    // 诊断开始时，诊断ctx对象创建锁
    std::mutex ctx_gen_mtx_;
    // 设置已经加载程序的锁
    std::mutex loaded_program_mtx_;
    // 处理诊断数据收集，压缩的线程
    DataCollectorDumpWorker dump_worker_;
    // 存放触发诊断的错误码, 依次触发诊断或延时
    DataCollectorErrcodeWorker errcode_worker_;
    // 诊断全局配置
    DiagConfig diag_config_;
    // 当次诊断ctx对象监听指针
    std::weak_ptr<DataCollectorContext> p_context_;
    TimedCANMsgQueueType can_msg_queue;
    std::string loaded_prog_name;
};

const std::string DataCollector::BASE_DIAGNOSIS_DIR = ZUC_DIAGNOSIS_DIR;
std::shared_ptr<DataCollector> DataCollector::s_instance_;
std::mutex DataCollector::s_instance_mtx_;

int32_t DiagConfig::Init(const std::string& usersettings)
{
    Util::IniParser diagConfigIni(usersettings.c_str());
    int32_t ret = 0;
    int32_t val;

    if (diagConfigIni.get(DIAG_BASE_DUR_KEY, val, DEF_DIAG_BASE_DUR, 1, DEF_DIAG_MAX_DUR) != 0)
    {
        diagConfigIni.set(DIAG_BASE_DUR_KEY, static_cast<int32_t>(DEF_DIAG_BASE_DUR));
        ret = -1;
    }
    else
    {
        baseDur = val;
    }

    if (diagConfigIni.get(DIAG_MAX_DUR_KEY, val, DEF_DIAG_MAX_DUR, 1, DEF_DIAG_MAX_DUR) != 0)
    {
        diagConfigIni.set(DIAG_MAX_DUR_KEY, static_cast<int32_t>(DEF_DIAG_MAX_DUR));
        ret = -1;
    }
    else
    {
        maxDur = val;
    }

    if (diagConfigIni.get(DIAG_STEP_KEY, val, DEF_DIAG_STEP_DUR, 1, DEF_DIAG_STEP_DUR) != 0)
    {
        diagConfigIni.set(DIAG_STEP_KEY, static_cast<int32_t>(DEF_DIAG_STEP_DUR));
        ret = -1;
    }
    else
    {
        stepDur = val;
    }

    static_assert(DEF_DIAG_MAX_FILE_BYTES <= 0x7FFFFFFF);

    if (diagConfigIni.get(DIAG_MAX_FILE_BYTES_KEY, val, DEF_DIAG_MAX_FILE_BYTES, 128, DEF_DIAG_MAX_FILE_BYTES) != 0)
    {
        diagConfigIni.set(DIAG_MAX_FILE_BYTES_KEY, static_cast<int32_t>(DEF_DIAG_MAX_FILE_BYTES));
        ret = -1;
    }
    else
    {
        maxFileBytes = val;
    }

    return ret;
}

DataCollectorContext::DataCollectorContext(uint32_t ecode) : trigger_time(std::chrono::steady_clock::now())
{
    this->should_stop_diag.store(false);

    auto p_data_collector = DataCollector::Ref().lock();
    if (__builtin_expect(!p_data_collector, 0))
    {
        this->start_time = this->trigger_time;
        this->end_time = this->trigger_time;
        return;
    }

    const DiagConfig& diag_config_ = p_data_collector->GetConfig();

    if (ecode == 0)
    {
        // 错误码为0 手动触发诊断
        this->type = TriggerType::MANUAL;
        this->start_time = this->trigger_time;
        this->end_time = this->trigger_time + std::chrono::seconds(diag_config_.BaseDur());
    }
    else
    {
        // 错误码不为0 自动触发诊断
        this->type = TriggerType::AUTO;
        uint32_t advanceDur = static_cast<uint32_t>(diag_config_.BaseDur() * diag_config_.AdvanceTimePercentage() / 100.0);
        this->start_time = this->trigger_time - std::chrono::seconds(advanceDur);
        this->end_time = this->trigger_time + std::chrono::seconds(diag_config_.BaseDur());
    }

    this->ecode = ecode;
}

DataCollectorContext::~DataCollectorContext() { should_stop_diag.store(true); }

void DataCollectorContext::Delay()
{
    auto p_data_collector = DataCollector::Ref().lock();
    if (__builtin_expect(!p_data_collector, 0))
        return;

    const DiagConfig diag_config_ = p_data_collector->GetConfig();
    auto expectVal = end_time.load();
    auto desiredVal = expectVal + std::chrono::seconds(diag_config_.StepDur());

    uint32_t dur = std::chrono::duration_cast<std::chrono::seconds>(desiredVal - trigger_time).count();
    if (dur > diag_config_.MaxDur())
        return;

    end_time.compare_exchange_strong(expectVal, desiredVal);
}

void DataCollectorDumpWorker::Loop()
{
    int32_t i = 0;

    while (!loopStop.load())
    {
        std::shared_ptr<DataCollectorContext> ctx;

        {
            std::unique_lock<std::mutex> uq_lk(cv_mtx);
            // 持续等待，直到ctx不为空，诊断开始
            cv.wait(uq_lk, [this]() -> bool { return bool(p_ctx) || loopStop.load(); });
            ctx = std::move(p_ctx);
        }

        if (__builtin_expect(loopStop.load(), 0))
            break;

        // 在当前代码中，诊断退出时，DataCollectorDumpWorker后于DataCollector释放，所以需判空
        auto p_data_collector = DataCollector::Ref().lock();
        if (__builtin_expect(!p_data_collector, 0))
            break;

        auto diag_config_ = p_data_collector->GetConfig();

        zucInnerDbg("= = = = = = Start = = = = = =\n");

        maxDiagFileId += 1;

        const std::string& diagDirName = FormatDiagFileName(maxDiagFileId, ctx->Ecode());
        std::string diagDirAbsPath = DataCollector::BASE_DIAGNOSIS_DIR + '/' + diagDirName;
        uint64_t diagFileUpperLimit = diag_config_.MaxDiagByte();

        if (DiagOSAPI::Mkdir(diagDirAbsPath) == 0)
        {
            CollectSysInfo(p_data_collector, ctx, diagDirAbsPath);
            CollectCanData(ctx, diagDirAbsPath);
            CollectLog(ctx, diagDirAbsPath);
            CollectUerConfig(ctx, diagDirAbsPath);
            CollectProgram(ctx, diagDirAbsPath);
            CompressDir(DataCollector::BASE_DIAGNOSIS_DIR, diagDirName);
        }

        // 执行回滚
        RotateDiagnosisPack(DataCollector::BASE_DIAGNOSIS_DIR, diagFileUpperLimit);

        zucInnerDbg("= = = = = = End = = = = = =\n");
        // ctx被自动释放，此时DataCollector中的weakptr监听到ctx为空，诊断结束
    }
}

void DataCollectorDumpWorker::DumpHardwareStatus(std::ostream& os, const std::shared_ptr<DataCollector>& pCollector)
{
    // TODO: 此处需转换为可读的形式
    os << "SCB Version: " << pCollector->scb_ver_.majorVer << ", " << pCollector->scb_ver_.minorVer << "\n";
    os << "Servo Version: " << pCollector->servo_ver_.ver << "\n";
}

void DataCollectorDumpWorker::CollectCanData(std::shared_ptr<DataCollectorContext>& ctx, const std::string& dir)
{
    auto start = std::chrono::steady_clock::now();
    std::string canCsvFilename = dir + "/can_data.csv";
    std::ofstream canLogFile(canCsvFilename, std::ios::out | std::ios::trunc);
    std::stringstream canMsgStreamBuf;
    auto p_data_collector = DataCollector::Ref().lock();
    if (__builtin_expect(!p_data_collector, 0))
    {
        return;
    }

    if (!canLogFile.is_open())
    {
        zucInnerDbg("Open %s failed.\n", canCsvFilename.c_str());
        return;
    }

    TimedCANMsgQueueType* can_msg_queue = p_data_collector->CANMsgQueue();

    canLogFile << "Channel,Type,Format,ID,TimeStamp,Data\n";

    auto start_time = std::chrono::steady_clock::now();
    int32_t next_dur = 5;
    while (true)
    {
        auto now = std::chrono::steady_clock::now();

        // 当前时间已经超过诊断时间限制，停止诊断
        if (ctx->CheckTimeExceedUpperLimit(now))
        {
            zucInnerDbg("%s:%d", __FUNCTION__, __LINE__);
            ctx->StopDiag(DataCollectorContext::DiagStopReason::TIMEOUT_STOP);
        }

        int32_t dur = std::chrono::duration_cast<std::chrono::seconds>(now - start_time).count();
        if (dur >= next_dur && !ctx->ShouldStopDiag())
        {
            zucInnerDbg("delay dur: %ld \n", dur);
            next_dur += 5;
        }

        TimedCANMsgType canMsg;
        bool dataReady = (can_msg_queue->Pop(canMsg) == 0);
        if (!dataReady)
        {
            // 队列为空，且处于停止状态，退出can报文收集循环
            if (ctx->ShouldStopDiag())
            {
                break;
            }
            else
            {
                std::this_thread::sleep_for(std::chrono::milliseconds(2));
                continue;
            }
        }

        // 检测到报文时间早于诊断所需报文时间
        if (ctx->CheckTimeExceedLowerLimit(canMsg.first))
        {
            continue;
        }

        // 检测到当前报文时间已经晚于诊断所需的报文时间, 退出循环
        if (ctx->CheckTimeExceedUpperLimit(canMsg.first))
        {
            break;
        }

        canMsgStreamBuf.str("");
        WriteCANMsg(canMsgStreamBuf, canMsg.second);

        if (!canLogFile)
            canLogFile.clear();

        canLogFile << canMsgStreamBuf.str();
    }

    auto end = std::chrono::steady_clock::now();
    auto ms_count = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
    zucInnerDbg("use time: %ldms.\n", ms_count);
}

void DataCollectorDumpWorker::CollectProgram(std::shared_ptr<DataCollectorContext>& ctx, const std::string& dir)
{
    zucInnerDbg(">> Collect program ...\n");
    auto p_data_collector = DataCollector::Ref().lock();
    if (__builtin_expect(!p_data_collector, 0))
    {
        return;
    }

    const std::string& loaded_prog_name = p_data_collector->GetLoadedProgName();
    if (loaded_prog_name.empty())
    {
        return;
    }

    auto pos = loaded_prog_name.find_last_of("/\\");
    if (pos == std::string::npos)
    {
        zucInnerDbg("CollectProgram: Copy from %s to %s\n", loaded_prog_name.c_str(), dir.c_str());
        DiagOSAPI::CopyFile(loaded_prog_name, dir);
        return;
    }

    const std::string progDirName = loaded_prog_name.substr(0, pos);
    zucInnerDbg("CollectProgram: Copy from %s to %s\n", progDirName.c_str(), dir.c_str());
    DiagOSAPI::CopyDir(progDirName, dir);
}

void DataCollectorDumpWorker::LoadMaxDiagFileId()
{
    std::vector<std::string> filelist;
    DiagOSAPI::ListFile(DataCollector::BASE_DIAGNOSIS_DIR, filelist, false);
    maxDiagFileId = 0;

    for (auto p = filelist.begin(); p != filelist.end(); p++)
    {
        DiagFileInfo diagFileInfo;
        std::string filename = p->substr(DataCollector::BASE_DIAGNOSIS_DIR.size() + 1);
        if (ParseDiagFileInfo(filename, diagFileInfo) != 0)
        {
            continue;
        }

        uint64_t id;
        try
        {
            id = std::stoull(diagFileInfo.id);
        }
        catch (std::logic_error const& e)
        {
            zucInnerDbg("LoadMaxDiagFileId: Failed to std::stoull(%s)\n", diagFileInfo.id.c_str());
            id = 0;
        }

        maxDiagFileId = std::max(id, maxDiagFileId);
    }
    zucInnerDbg("LoadMaxDiagFileId: current max diag filed id is (%llu)\n", maxDiagFileId);
}

int32_t DataCollectorDumpWorker::RotateDiagnosisPack(const std::string& curWorkDir, uint64_t diagFileUpperLimit)
{
    uint64_t sz;
    int32_t ret = DiagOSAPI::DirSize(curWorkDir, sz);
    if (ret == -1)
    {
        zucInnerDbg("RotateDiagnosisPack: Failed to get dirsize.\n", curWorkDir.c_str());
        return -1;
    }

    zucInnerDbg("current size: %llu\n", sz);
    if (sz <= diagFileUpperLimit)
    {
        return 0;
    }

    std::vector<std::string> dirlist;
    DiagOSAPI::ListDir(curWorkDir, dirlist, false);

    std::vector<std::string> filelist;
    if (0 != DiagOSAPI::ListFile(curWorkDir, filelist, false))
    {
        return -1;
    }

    // 根据PQTypeGreater中的规则生成优先级队列
    std::priority_queue<FileInfoPair, std::vector<FileInfoPair>, PQTypeGreater> pq;
    std::for_each(filelist.begin(), filelist.end(), [&](const std::string& x) {
        DiagFileInfo fileinfo;
        auto packName = x.substr(DataCollector::BASE_DIAGNOSIS_DIR.size() + 1);
        ParseDiagFileInfo(packName, fileinfo);
        pq.push(std::make_pair(fileinfo, x));
    });

    for (int i = 0; i < 5; i++)
    {
        if (!dirlist.empty())
        {
            auto dirpath = dirlist.back();
            dirlist.pop_back();
            DiagOSAPI::RemoveF(dirpath);
        }

        if (__builtin_expect(pq.empty(), 0))
        {
            zucInnerDbg("RotateDiagnosisPack: No Diagnosis File.\n");
            return -1;
        }

        // 优先删除有高删除优先级的诊断日志
        auto fileInfoPair = pq.top();
        pq.pop();

        auto removeFilePath = fileInfoPair.second;
        zucInnerDbg("RotateDiagnosisPack: RemoveF %s.\n", removeFilePath.c_str());
        DiagOSAPI::RemoveF(fileInfoPair.second);

        ret = DiagOSAPI::DirSize(curWorkDir, sz);
        if (ret == -1)
        {
            zucInnerDbg("RotateDiagnosisPack: Failed to get dirsize.\n", curWorkDir.c_str());
            return -1;
        }

        if (sz <= diagFileUpperLimit)
        {
            break;
        }
    }

    return 0;
}

void DataCollectorErrcodeWorker::Loop()
{
    while (!loopStop)
    {
        uint32_t ecode = 0;

        {
            std::unique_lock<std::mutex> uq_lk(cv_mtx);
            cv.wait(uq_lk, [this, &ecode]() -> bool { return !queue.Pop(ecode) || loopStop.load(); });
        }

        if (__builtin_expect(loopStop.load(), 0))
            break;

        auto p_data_collector = DataCollector::Ref().lock();
        if (__builtin_expect(!p_data_collector, 0))
            break;

        p_data_collector->Start(ecode);
    }
}

int32_t zucDiagCollectData(int32_t flag)
{
    int32_t ret = 0;
    auto pCollector = DataCollector::Ref().lock();
    if (!pCollector)
    {
        return -1;
    }

    if (flag == 1)
        ret = pCollector->Start(0);
    else
        ret = pCollector->Stop();

    return ret;
}

int32_t zucDiagIsCollectingData()
{
    auto pCollector = DataCollector::Ref().lock();

    if (!pCollector)
    {
        return -1;
    }

    return pCollector->IsCollecting();
}

int32_t zucDiagSetLoadedProgram(const char* file)
{
    auto pCollector = DataCollector::Ref().lock();

    if (!pCollector)
    {
        return -1;
    }

    if (file)
    {
        zucInnerDbg("Set Program: %s\n", file);
        return pCollector->SetLoadedProgName(file);
    }
    else
    {
        zucInnerDbg("Set Program: NULL \n");
        return pCollector->SetLoadedProgName("");
    }
}
int32_t zucDiagBus(const zucDiagBusMsg& msg)
{
    auto pCollector = DataCollector::Ref().lock();

    if (!pCollector)
    {
        return -1;
    }
    return pCollector->Push(msg);
}

int32_t zucDiagEcode(uint32_t ecode)
{
    auto pCollector = DataCollector::Ref().lock();
    if (!pCollector)
    {
        return -1;
    }
    return pCollector->Push(ecode);
}

int32_t zucDiagInit()
{
    DataCollector::Init();
    auto pCollector = DataCollector::Ref().lock();
    if (!pCollector)
    {
        return -1;
    }
    return 0;
}

int32_t zucDiagDeInit()
{
    auto pCollector = DataCollector::Ref().lock();
    if (!pCollector)
    {
        return -1;
    }
    return DataCollector::Reset();
}

int32_t zucDiagUpdateVerInfo(zucDiagVerType verType, zucDiagVerData verData)
{
    auto pCollector = DataCollector::Ref().lock();
    if (!pCollector)
    {
        return -1;
    }

    switch (verType)
    {
    case zucDiagVerType::SCB_VERSION:
        pCollector->scb_ver_ = verData.scb;
        break;

    case zucDiagVerType::SERVO_VERSION:
        pCollector->servo_ver_ = verData.servo;
        break;

    default:
        return -1;
    }

    return 0;
}

int32_t zucDiagConfig(uint32_t baseDur, uint32_t stepDur, uint32_t maxDur)
{
    auto pCollector = DataCollector::Ref().lock();
    if (!pCollector)
    {
        return -1;
    }

    DiagConfig& config = pCollector->GetConfig();
    config.baseDur = baseDur;
    config.maxDur = maxDur;
    config.stepDur = stepDur;

    return 0;
}